home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Plotter / PlotView.m < prev    next >
Text File  |  1995-06-12  |  9KB  |  343 lines

  1. /* PlotView.m -- Implementation file for the PlotView class */
  2.  
  3. #import "PlotView.h"
  4. #import "drawingFuncs.h"
  5. #import <objc/Storage.h>
  6. #import <appkit/NXCursor.h>
  7. #import <appkit/Application.h>
  8. #import <appkit/Window.h>
  9. #import <appkit/Cell.h>
  10. #import <appkit/Pasteboard.h>
  11. #import <NXCType.h> 
  12. #import <math.h> 
  13. #import <dpsclient/psops.h>
  14. #import <dpsclient/wraps.h>
  15. #import <stdlib.h> 
  16. #import <string.h> 
  17.  
  18. extern float getNumber(NXStream *stream);
  19. extern int getSeparator(NXStream *stream);
  20.  
  21. #define MAXNUMLENGTH 50
  22. #define DEFAULTRADIUS 1.0
  23.  
  24. @implementation PlotView
  25.  
  26. - initFrame:(const NXRect *)frameRect
  27. /*
  28.  * Initializes the new PlotView object.  First, an initFrame: message is sent to 
  29.  * super to initialize PlotView as a View.  Next, the PlotView sets its own 
  30.  * state -- that it is opaque and that the origin of its coordinate system lies in 
  31.  * the center of its area.  It then creates and initializes its associated objects,
  32.  * a Storage object, an NXCursor, and a Cell.  Finally, it loads into the Window 
  33.  * Server some PostScript procedures that it will use in drawing itself. 
  34.  */
  35. {
  36.     NXPoint    spot;
  37.     
  38.     [super initFrame:frameRect];
  39.     [self setOpaque:YES];
  40.     [self setRadius:DEFAULTRADIUS];
  41.     [self translate:floor(frame.size.width/2) :floor(frame.size.height/2)];
  42.     points = [[Storage alloc] initCount:0 elementSize:sizeof(NXPoint) description:"{ff}"];
  43.     crossCursor = [NXCursor newFromImage:[NXImage newFromSection:"cross.tiff"]];
  44.     spot.x = spot.y = 7.0;
  45.     [crossCursor setHotSpot:&spot];
  46.     /* 
  47.      * Normally, the next two message expressions would be combined, as:
  48.      *         readOut = [[Cell alloc] initTextCell:""];
  49.      * but are here separated for clarity.
  50.      */
  51.     readOut = [Cell alloc];
  52.     [readOut initTextCell:""];
  53.     [readOut setBezeled:YES];
  54.     loadPSProcedures();
  55.     return self;
  56. }
  57.  
  58.  
  59. - setDelegate:anObject
  60. /*
  61.  * Sets the PlotView's delegate instance variable to the supplied object.
  62.  */
  63. {
  64.     delegate = anObject;
  65.     return self;
  66. }
  67.  
  68.  
  69. - drawSelf:(const NXRect *)rects :(int)rectCount
  70. /*
  71.  * Draws the PlotView's background and axes.  If there are any points,
  72.  * these are drawn too.
  73.  */
  74. {
  75.     unsigned int     i;
  76.     NXPoint        *aPoint;
  77.     
  78.     if (rects == NULL) return self;
  79.  
  80.     /* If we're printing, we need to load drawing procedures to printing context */
  81.     if (NXDrawingStatus != NX_DRAWING) 
  82.         loadPSProcedures();
  83.  
  84.     /* paint visible area white then draw axes */
  85.     PSsetgray(NX_WHITE);
  86.     NXRectFill(&rects[0]);
  87.     PSsetgray(NX_DKGRAY);
  88.     drawAxes(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
  89.  
  90.     /* now take each point and draw it */
  91.     PSsetgray(NX_BLACK);
  92.     i = [points count];
  93.     while (i--) {
  94.         aPoint = (NXPoint *)[points elementAt:i];
  95.         drawCircle(aPoint->x, aPoint->y, radius);
  96.     } 
  97.     return self;
  98. }
  99.  
  100. - clear:sender
  101. /*
  102.  * Clears the PlotView by emptying its Storage object of all points
  103.  * and then redisplaying the PlotView.  This action is taken only if
  104.  * the PlotView's state requires it.
  105.  */
  106. {
  107.     if (needsClearing) {
  108.         [points empty];
  109.         [self display];
  110.         needsClearing = NO;
  111.     }
  112.     return self;
  113. }
  114.  
  115. - sizeTo:(NXCoord)width :(NXCoord)height
  116. /*
  117.  * Ensures that whenever the PlotView is resized, the origin of its 
  118.  * coordinate system is repositioned to the center of its area.
  119.  */
  120. {
  121.     [super sizeTo:width :height];
  122.     [self setDrawOrigin:-floor(width/2) : -floor(height/2)]; 
  123.     return self;
  124. }
  125.  
  126. - registerPoint:(NXPoint *)aPoint
  127. /*
  128.  * Adds a point to the list the PlotView keeps in its Storage object. 
  129.  */
  130. {
  131.     [points addElement:aPoint];
  132.     return self;
  133. }
  134.  
  135. - mouseDown:(NXEvent *) theEvent
  136. /*
  137.  * Responds to a message the system sends whenever the user presses the mouse
  138.  * button when the cursor is over the PlotView.  The PlotView changes the 
  139.  * cursor to a cross-hairs image and then starts asking for mouse-dragged or mouse-
  140.  * up events.  As it receives mouse-dragged events, the PlotView updates the readOut
  141.  * text Cell with the cursor's coordinates.  If the user releases the mouse
  142.  * button while the cursor is over the PlotView, the PlotView registers the 
  143.  * point and then sends a message to its delegate notifying it of the new 
  144.  * point.
  145.  */
  146. {
  147.     int    looping = YES, oldMask;
  148.     NXPoint    aPoint;
  149.     NXRect    plotRect, cellRect;
  150.        char    buffer[100];
  151.  
  152.     [crossCursor set];
  153.        [self getBounds:&plotRect];
  154.      NXSetRect(&cellRect, plotRect.origin.x, plotRect.origin.y, 100.0, 20.0);
  155.  
  156.     oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  157.     [self lockFocus];
  158.     do {
  159.         aPoint = theEvent->location;
  160.         [self convertPoint:&aPoint fromView:nil];
  161.         sprintf(buffer, "(%d, %d)", (int)aPoint.x, (int)aPoint.y); 
  162.           [readOut setStringValue:buffer];
  163.           [readOut drawInside:&cellRect inView:self];
  164.         [window flushWindow];
  165.         if (theEvent->type == NX_MOUSEUP) {
  166.             /* on mouse-up, register point, inform delegate, and clean up state */
  167.             [readOut setStringValue:""];
  168.               [readOut drawInside:&cellRect inView:self];
  169.             [window flushWindow];
  170.             if (NXPointInRect(&aPoint, &plotRect)) {
  171.                 PSsetgray(NX_BLACK);
  172.                 drawCircle(aPoint.x, aPoint.y, radius);
  173.                 [window flushWindow];
  174.                 [self registerPoint:&aPoint];
  175.                 needsClearing = YES;
  176.                 if (delegate && [delegate respondsTo:@selector(plotView:pointDidChange:)])
  177.                     [delegate plotView:self pointDidChange:&aPoint];
  178.             }
  179.             looping = NO;
  180.         }    
  181.        } while (looping && (theEvent=[NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK]));
  182.     [self unlockFocus];
  183.     [window setEventMask:oldMask];
  184.     [NXArrow set];
  185.     return self;
  186. }
  187.  
  188. - setRadius:(float)aFloat
  189. /*
  190.  * Sets the value for the radius of the PlotView's points.
  191.  */
  192. {
  193.     radius = aFloat;
  194.     return self;
  195. }
  196.  
  197.  
  198. - (float)radius
  199. /*
  200.  * Returns the value for the radius of the PlotView's points.
  201.  */
  202. {
  203.     return radius;
  204. }
  205.  
  206.  
  207. - read:(NXTypedStream*)stream
  208. /*
  209.  * Unarchives the PlotView.  Initializes four of the PlotView's instance variables
  210.  * to the values stored in the stream. 
  211.  */
  212. {
  213.     [super read:stream];
  214.     delegate = NXReadObject(stream);
  215.     NXReadTypes(stream, "@@@f", &points, &crossCursor, &readOut, &radius);
  216.     return self;
  217. }
  218.  
  219. - write:(NXTypedStream*)stream
  220. /*
  221.  * Archives the PlotView by writing its important instance variables to the stream. 
  222.  */
  223. {
  224.     [super write:stream];
  225.     NXWriteObjectReference(stream, delegate);
  226.     NXWriteTypes(stream, "@@@f", &points, &crossCursor, &readOut, &radius);
  227.     return self;
  228. }
  229.  
  230. - awake
  231. /*
  232.  * Finishes initializing a PlotView that has just been unarchived (see the read: method).
  233.  */
  234. {
  235.     loadPSProcedures();
  236.     return [super awake];
  237. }
  238.  
  239.  
  240. - (const char*)inspectorName
  241. {
  242.     return "PlotViewInspector";
  243. }
  244.  
  245. - plot:sender
  246. /*
  247.  * Responds to a plot: message by asking the PlotView's delegate for a stream
  248.  * containing the points to plot.  It then extracts number pairs from the stream
  249.  * and creates NXPoint structures with them.  For each structure it creates, the 
  250.  * PlotView sends itself a registerPoint: message to add the point to its list of 
  251.  * points.  This simple parser ignores input it doesn't understand.
  252.  */
  253. {
  254.     int          c, sign, retval;
  255.     float          aNum;
  256.     NXPoint          aPoint;
  257.     NXStream    *stream;
  258.     BOOL         processedLine = NO, gotFirst = NO, gotSecond = NO;
  259.     
  260.     if (delegate && [delegate respondsTo:@selector(plotView:providePoints:)])
  261.         [delegate plotView:self providePoints:&stream];
  262.     NXSeek(stream, 0, NX_FROMSTART);
  263.     while ((c = NXGetc(stream)) != EOF){
  264.         sign = 1;
  265.         retval = getSeparator(stream);
  266.         if (retval == 1) {
  267.             c = NXGetc(stream);
  268.         } else if (retval == -1) {
  269.             break;
  270.         }
  271.         if (!NXIsDigit(c)) {
  272.             if ((c == '-') && NXIsDigit(c = NXGetc(stream))){
  273.                     sign = -1;
  274.             } else {
  275.                 while( (c != '\n') && (c != EOF)) {
  276.                     c = NXGetc(stream);
  277.                     processedLine = YES;
  278.                 }
  279.             }
  280.         }
  281.         if (c == EOF)
  282.             break;
  283.         else if (processedLine) {
  284.             processedLine = NO;
  285.             continue;
  286.         }
  287.         aNum = getNumber(stream);
  288.         if (!gotFirst) {
  289.             aPoint.x = sign * aNum;
  290.             gotFirst = YES;
  291.         } else if (!gotSecond) {
  292.             aPoint.y = sign * aNum;
  293.             [self registerPoint:&aPoint];
  294.             gotFirst = gotSecond = NO;
  295.         } 
  296.     }
  297.     [self display];
  298.     needsClearing = YES;
  299.     return self;
  300. }
  301.  
  302. int getSeparator(NXStream *stream)
  303. /* 
  304.  * Removes white space, commas, and plus signs from between 
  305.  * number pairs.
  306.  */
  307. {
  308.     int c, firstChar;
  309.     
  310.     NXUngetc(stream);
  311.     c = firstChar = NXGetc(stream);
  312.     while (NXIsSpace(c) || (c == ',') || (c == '+'))
  313.         c = NXGetc(stream);
  314.     /* 1 = ate something; 0 = ate nothing; -1 means EOF */
  315.     if (c == firstChar)
  316.         return 0;
  317.     else if (c == EOF)
  318.         return -1;
  319.     NXUngetc(stream);
  320.     return 1;
  321. }
  322.  
  323. float getNumber(NXStream *stream)
  324. /* 
  325.  * Composes a floating point number from a string of number  
  326.  * characters.
  327.  */
  328. {
  329.     int c, i = 0;
  330.     char temp[MAXNUMLENGTH];
  331.     
  332.     NXUngetc(stream);
  333.     c = NXGetc(stream);
  334.     while ((NXIsDigit(c) || (c == '.')) && (c != '\n') && (c != EOF)) {
  335.         temp[i++] = c;
  336.         c = NXGetc(stream);
  337.     }
  338.     NXUngetc(stream);
  339.     temp[i] = 0;
  340.     return (atof(temp));
  341. }
  342.  
  343. @end